/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package stud.cp.user.order;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import java.util.logging.Level;
import javax.ejb.Stateless;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import stud.cp.core.dbaccess.DAOException;
import stud.cp.core.plugins.PluginConst;
import stud.cp.core.plugins.PluginRouter;
import stud.cp.core.plugins.WrongPageRequest;
import stud.cp.user.dbaccess.*;
import stud.cp.user.valueobjects.DBObject;
import stud.cp.user.valueobjects.DBObjectReference;
import stud.cp.user.valueobjects.DBObjectType;
import stud.cp.user.valueobjects.Parameter;

/**
 *
 * @author morfant
 */
@Stateless
public class OrderPluginBean implements OrderPluginBeanLocal {

    /**
     * Фабрика DAO классов.
     */
    private OracleDAOFactory oracleDAOFactory = null;
    /**
     * Логгер.
     */
    private static Logger logger = Logger.getLogger("ACPII");

    /**
     * Метод, выполняющий бизнес логику плагина.
     *
     * @param req Реквест.
     * @return Класс плагин.
     */
    @Override
    public PluginRouter perform(HttpServletRequest req) {
        String subaction = req.getParameter(PluginConst.SUBACTION);


        if ((subaction != null) && (!subaction.equals(""))) {
            try {

                oracleDAOFactory = new OracleDAOFactory();
                oracleDAOFactory.initialize();
                /*
                 * Список действий которые могут выполниться.
                 */

                if (subaction.equals(PluginConst.GET_LIST)) {
                    return getListOfDishes(req);
                }

                /*
                 * Страница добавления
                 */
                if (subaction.equals(PluginConst.GET_ADD_PAGE)) {
                    return getAddPage(req);
                }

                /*
                 * Добавление новых данных
                 */
                if (subaction.equals(PluginConst.NEW_DATA)) {
                    return newData(req);
                }

                /*
                 * Страница редактирования
                 */
                if (subaction.equals(PluginConst.GET_EDIT_PAGE)) {
                    return getEditPage(req);
                }

                /*
                 * Сохранение измененных данных
                 */
                if (subaction.equals(PluginConst.SAVE_DATA)) {
                    return saveData(req);
                }

                /*
                 * Страница удаления
                 */
                if (subaction.equals(PluginConst.GET_DELETE_PAGE)) {
                    return getDeletePage(req);
                }

                /*
                 * Удаление данные
                 */
                if (subaction.equals(PluginConst.DELETE_DATA)) {
                    return deleteData(req);
                }

                /*
                 * Страница просмотра данных о типе оружия
                 */
                if (subaction.equals(PluginConst.GET_VIEW_PAGE)) {
                    return getViewPage(req);
                }

                if (subaction.equals(PluginConst.FINISH_ORDER)) {
                    return finishOrder(req);
                }

                if (subaction.equals(PluginConst.TERMINATE_ORDER)) {
                    return terminateOrder(req);
                }

                //                /*Страница со списком свойств, принадлежащий данному типу оружия.*/
                //                if (subaction.equals(PluginConst.GET_WEAPON_TYPE_PROPERTIES_LIST)) {
                //                    return getObjectTypePropertyListPage(req);
                //                }
                //
                //                /*Страница с диалогом для пользователя, который хочет исключить свойство из типа оружия.*/
                //                if (subaction.equals(PluginConst.EXCLUDE_PROPERTY_FROM_WEAPON_TYPE_PAGE)) {
                //                    return getExcludePropertyFromObjectTypePage(req);
                //                }
                //
                //                /*Исключение свойства из объектного типа.*/
                //                if (subaction.equals(PluginConst.EXCLUDE_PROPERTY_FROM_WEAPON_TYPE)) {
                //                    return excludePropertyFromObjectType(req);
                //                }
                //
                //                /*Страница добавления свойств к типу оружия.*/
                //                if (subaction.equals(PluginConst.ADD_PROPERTIES_TO_WEAPON_TYPE_PAGE)) {
                //                    return getAddPropertiesToObjectTypePage(req);
                //                }
                //
                //                /*Добавление свойств к объектному типу*/
                //                if (subaction.equals(PluginConst.ADD_PROPERTIES_TO_WEAPON_TYPE)) {
                //                    return addPropertiesToObjectType(req);
                //                }

                req.setAttribute(PluginConst.ERROR_MESSAGE,
                        "Действия для данного типа поддействия не найдено. Тип поддействия = " + subaction);
            } catch (DAOException ex) {
                logger.debug("Error due access to database. "
                        + "Plugin = ProductTypesPluginBean. "
                        + "Subaction=" + subaction, ex);
                req.setAttribute(PluginConst.ERROR_MESSAGE,
                        "Ошибка доступа к базе данных " + ex.getMessage());
            } catch (WrongPageRequest ex) {
                req.setAttribute(PluginConst.ERROR_MESSAGE,
                        "Данные из реквеста ошибочны :\n" + ex.getMessage());
            } catch (NumberFormatException ex) {
                req.setAttribute(PluginConst.ERROR_MESSAGE,
                        "ID объекта не имеет числовой формат :\n" + ex.getMessage());
            } finally {

                if (oracleDAOFactory != null) {
                    try {
                        oracleDAOFactory.cleanUp();
                    } catch (DAOException ex) {
                        logger.debug("Error due access to database. "
                                + "Plugin = ProductTypesPluginBean. "
                                + "Subaction=" + subaction, ex);
                        req.setAttribute(PluginConst.ERROR_MESSAGE,
                                "Ошибка доступа к базе данных " + ex.getMessage());
                    }
                }
            }
        } else {
            req.setAttribute(PluginConst.ERROR_MESSAGE,
                    "Тип поддействия не задан.");
        }

        return new PluginRouter("/errorpage.jsp");
    }

    private PluginRouter getListOfDishes(HttpServletRequest req) throws DAOException, WrongPageRequest {

        DBObjectType objectType = oracleDAOFactory.getObjectTypesDAO().getById(PluginConst.ORDER_OBJECT_TYPE_ID);

        req.setAttribute(PluginConst.OBJECT_TYPE, objectType);

        List<DBObject> listOfDBObject =
                oracleDAOFactory.getObjectDAO().getAllObjectByObjectTypeButNotDelete(objectType.getObject_type_id());
        Map<BigInteger, String> dishStatusMap = new HashMap<BigInteger, String>();

        ParametersDAO parameterDAO = oracleDAOFactory.getParameterDAO();
        Parameter param;

        for (DBObject dish : listOfDBObject) {
            param = parameterDAO.getParameterByIdForObject(PluginConst.ORDER_STATUS_PROPERTY_ID, dish.getObject_id());
            dishStatusMap.put(dish.getObject_id(), param.getProperty_list_value());
        }


        req.setAttribute(PluginConst.OBJECT_LIST, listOfDBObject);
        req.setAttribute(PluginConst.DISH_STATUS_MAP, dishStatusMap);

        return new PluginRouter("/Order/OrdersTable.jsp");

    }

    private PluginRouter getAddPage(HttpServletRequest req) throws DAOException, WrongPageRequest {

        DBObjectType objectType = oracleDAOFactory.getObjectTypesDAO().getById(PluginConst.ORDER_OBJECT_TYPE_ID);

        //Сохраняем объектный тип заказа в реквест.
        req.setAttribute(PluginConst.OBJECT_TYPE, objectType);

        List<DBObjectType> distTypeList =
                oracleDAOFactory.getObjectTypesDAO().getObjectTypeHeirsButNotDelete(PluginConst.DISH_OBJECT_TYPE_ID);

        //Сохраняем на страницу список типов продуктов.
        req.setAttribute(DISH_OBJECT_TYPE_LIST, distTypeList);

        List<DBObject> distList;
        List<Parameter> dishParameterList;
        BigInteger dishObjectTypeId;

        ObjectDAO objectDAO = oracleDAOFactory.getObjectDAO();
        ParametersDAO paramDAO = oracleDAOFactory.getParameterDAO();
        //Для каждого типа блюда добавляем в реквест список блюд этого типа и параметры этих продуктов.
        for (DBObjectType productObjectType : distTypeList) {
            dishObjectTypeId = productObjectType.getObject_type_id();
            distList = objectDAO.getAllObjectByObjectTypeButNotDelete(dishObjectTypeId);
            //Добавляем для типа блюда список блюд.
            req.setAttribute(dishObjectTypeId.toString(), distList);
            for (DBObject product : distList) {
                //Для каждого блюда добавляем список его параметров.
                dishParameterList = paramDAO.getPropertyValueForObject(product.getObject_id());
                req.setAttribute(DISH_PARAMETER_LIST_FOR_PRODUCT + "=" + product.getObject_id().toString(), dishParameterList);
            }
        }
        return new PluginRouter("/Order/AddOrder.jsp");

    }
    private static final String DISH_XML_ROOT_ELEMENT = "DISH_XML_ROOT_ELEMENT";
    private static final String DISH_XML_PRODUCT_ELEMENT = "DISH_XML_PRODUCT_ELEMENT";
    private static final String DISH_XML_PRODUCT_ID_ATTRIBUTE = "DISH_XML_PRODUCT_ID_ATTRIBUTE";
    private static final String DISH_XML_PRODUCT_AMOUNT_ATTRIBUTE = "DISH_XML_PRODUCT_AMOUNT_ATTRIBUTE";
    private static final String PRODUCT_XML_ROOT_ELEMENT = "PRODUCT_XML_ROOT_ELEMENT";
    private static final String PRODUCT_XML_PRODUCT_ELEMENT = "PRODUCT_XML_PRODUCT_ELEMENT";
    private static final String PRODUCT_XML_PRODUCT_ID_ATTRIBUTE = "PRODUCT_XML_PRODUCT_ID_ATTRIBUTE";
    private static final String PRODUCT_XML_PRODUCT_AMOUNT_ATTRIBUTE = "PRODUCT_XML_PRODUCT_AMOUNT_ATTRIBUTE";

    /**
     * Метод, позволяющий добавлять новые данные.
     *
     * @param req Реквест.
     * @return PluginRouter c запрашиваемой страницей.
     * @throws DAOException Ошибка доступа к базе данных.
     * @throws WrongPageRequest Критическая ошибка в данных реквеста.
     */
    private PluginRouter newData(HttpServletRequest req) throws DAOException, WrongPageRequest {
        String objectTypeIdValue = req.getParameter(PluginConst.OBJECT_TYPE_ID);
        if ((objectTypeIdValue == null) || (objectTypeIdValue.equals(""))) {
            throw new WrongPageRequest("ID объекта не задан!");
        }
        //Получаем объектный тип блюда.
        BigInteger objectTypeId =
                new BigInteger(objectTypeIdValue);
        DBObjectType objectType = oracleDAOFactory.getObjectTypesDAO().getById(objectTypeId);

        Date date = new Date();
        String name = "Заказ " + date;
        String description = "";
        String orderCost = req.getParameter(PluginConst.ORDER_COST);

        String[] dishIds = req.getParameterValues(DISH_IDS);
        int productNumber = dishIds.length;

        List<BigInteger> dishIdList = new LinkedList<BigInteger>();
        Map<BigInteger, String> dishAmountMap = new HashMap<BigInteger, String>();

        Document document = new Document();
        Element rootElement = new Element(DISH_XML_ROOT_ELEMENT);
        String dishAmount;
        Element dishElement;
        if (objectType != null) {


            ObjectReferencesDAO objectReferencesDAO = oracleDAOFactory.getObjectReferencesDAO();

            BigInteger dishId;

            for (int i = 0; i < productNumber; i++) {
                dishId = new BigInteger(dishIds[i]);
                dishAmount = req.getParameter(PluginConst.AMOUNT + dishIds[i]);
                dishElement = new Element(DISH_XML_PRODUCT_ELEMENT);
                //ID блюда.
                dishIdList.add(dishId);
                dishElement.setAttribute(DISH_XML_PRODUCT_ID_ATTRIBUTE, dishIds[i]);
                //ID блюда + число единиц этого блюда.
                dishAmountMap.put(dishId, dishAmount);
                dishElement.setAttribute(DISH_XML_PRODUCT_AMOUNT_ATTRIBUTE, dishAmount);

                rootElement.addContent(dishElement);
            }

            document.setContent(rootElement);

            XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
            String recipeXMLString = outputter.outputString(document);

            ParametersDAO propertyValueDAO = oracleDAOFactory.getParameterDAO();

            Map<BigInteger, Double> productAmount = new HashMap<BigInteger, Double>();

            Double tempAmount;

            Double dishCount;


            for (BigInteger dish : dishIdList) {

                dishCount = new Double(dishAmountMap.get(dish));

                //Get recipe of dish
                //Create map with product and it is amount for this dish

                //Сбор данных для отображения положения дел для данного блюда.
                //Работаем с параметрами объекта - блюда.
                List<Parameter> parameterList = oracleDAOFactory.getParameterDAO().getPropertyValueForObject(dish);

                for (Parameter param : parameterList) {
                    if (param.getProperty_id().equals(PluginConst.RECIPE_PROPERTY_ID)) {
                        //Если это рецепт, то работаем с рецептом.
                        String recipeXML = param.getValue();
                        SAXBuilder builder = new SAXBuilder();
                        try {
                            //Разбираем рецепт.
                            //Парсим отношение продуктов и их количества из xml.
                            document = builder.build(new ByteArrayInputStream(recipeXML.getBytes()));
                            Element root = document.getRootElement();
                            List<Element> productElementList = root.getChildren(PRODUCT_XML_PRODUCT_ELEMENT);


                            BigInteger productId;
                            for (Element productElement : productElementList) {
                                productId = new BigInteger(
                                        productElement.getAttributeValue(PRODUCT_XML_PRODUCT_ID_ATTRIBUTE));

                                if (productAmount.containsKey(productId)) {
                                    tempAmount = productAmount.get(productId);
                                    productAmount.put(productId,
                                            tempAmount
                                            + dishCount * Double.valueOf(productElement.getAttributeValue(PRODUCT_XML_PRODUCT_AMOUNT_ATTRIBUTE)));
                                } else {
                                    productAmount.put(productId,
                                            dishCount * Double.valueOf(productElement.getAttributeValue(PRODUCT_XML_PRODUCT_AMOUNT_ATTRIBUTE)));
                                }
                            }
                        } catch (JDOMException ex) {
                            logger.debug("Ошибка во время разбора рецепта.", ex);
                        } catch (IOException ex) {
                            logger.debug("Ошибка во время разбора рецепта.", ex);
                        }
                    }
                }
            }

            try {
                oracleDAOFactory.getConnection().setAutoCommit(false);
                oracleDAOFactory.getConnection().setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

                Collection<BigInteger> productIdList = productAmount.keySet();

                List<Parameter> listOfProductStorageTypeParam = new LinkedList<Parameter>();
                Double productAmountInStorage;

                ObjectDAO objectDAO = oracleDAOFactory.getObjectDAO();

                String errorMessage = "";

                for (BigInteger productId : productIdList) {
                    Parameter productStorageType = propertyValueDAO.getParameterByIdForObject(PluginConst.STORE_TYPE_PROPERTY_ID, productId);
                    listOfProductStorageTypeParam.add(productStorageType);
                    productAmountInStorage = new Double(productStorageType.getValue());
                    if (productAmountInStorage >= productAmount.get(productId)) {
                        productStorageType.setValue((productAmountInStorage - productAmount.get(productId)) + "");
                    } else {
                        List<DBObject> redudantDishList =
                                objectDAO.getAllObjectThatLinkOnAnotherObjectWithFiltering(productId, dishIdList);
                        errorMessage += "Не хватает продукта - "
                                + objectDAO.getById(productId).getName()
                                + " ("
                                + Math.abs(productAmountInStorage - productAmount.get(productId))
                                + (productStorageType.getProperty_list_value_id().equals(new BigInteger("3"))
                                ? " кг. "
                                : " шт. ")
                                + ")."
                                + "<br/>Уменьшите количество порций блюд : ";
                        int i = 1;
                        int lenght = redudantDishList.size();
                        for (DBObject redudantDish : redudantDishList) {
                            errorMessage += redudantDish.getName();
                            if (i < lenght) {
                                errorMessage += ", ";
                                i++;
                            }
                        }
                        errorMessage += ". <br/>";

                    }
                }

                if (errorMessage.equals("")) {
                    BigInteger object_id =
                            oracleDAOFactory.getObjectDAO().
                            create(new DBObject(objectType.getObject_type_id(), name, description));

                    for (Parameter productStorageType : listOfProductStorageTypeParam) {
                        propertyValueDAO.update(productStorageType);
                    }

                    propertyValueDAO.create(new Parameter(PluginConst.ORDER_CONSIST_PROPERTY_ID,
                            recipeXMLString, object_id, null));
                    propertyValueDAO.create(new Parameter(PluginConst.ORDER_CONST_PROPERTY_ID,
                            orderCost, object_id, null));
                    propertyValueDAO.create(new Parameter(PluginConst.ORDER_STATUS_PROPERTY_ID,
                            null, object_id, PluginConst.ORDER_STATUS_IN_PROGRESS_LIST_VALUE_ID));
                    propertyValueDAO.create(new Parameter(PluginConst.ORDER_DATE_INCOME_PROPERTY_ID,
                            null, object_id, null, date));


                    for (BigInteger dishIdForLink : dishIdList) {
                        objectReferencesDAO.create(
                                new DBObjectReference(object_id,
                                dishIdForLink,
                                PluginConst.LINK_ON_DISH_PRODPERTY_ID));
                    }


                    //Создание расходов по продуктам для данного заказа.
                    productIdList = productAmount.keySet();

                    for (BigInteger productId : productIdList) {

                        BigInteger expense_object_id =
                                oracleDAOFactory.getObjectDAO().
                                create(new DBObject(PluginConst.EXPENSE_OBJECT_TYPE_ID, "EXPENCE " + name, ""));

                        propertyValueDAO.create(new Parameter(PluginConst.EXPENSE_DATE_PRODPERTY_ID,
                                null, expense_object_id, null, date));
                        propertyValueDAO.create(new Parameter(PluginConst.AMOUNT_OF_PRODUCT_EXPENSE_PRODPERTY_ID,
                                productAmount.get(productId) + "", expense_object_id, null));

                        objectReferencesDAO.create(
                                new DBObjectReference(expense_object_id,
                                productId,
                                PluginConst.LINK_EXPENSE_ON_PRODUCT_PROPERTY_ID));

                        objectReferencesDAO.create(
                                new DBObjectReference(object_id,
                                expense_object_id,
                                PluginConst.LINK_ON_EXPENSE_PRODPERTY_ID));
                    }
                    //Создание отчета о себистоимости и стоимости данного заказа.

                    Parameter param;
                    Double primeCostOfOrder = new Double(0);
                    for (BigInteger dish : dishIdList) {
                        param = propertyValueDAO.getParameterByIdForObject(PluginConst.DISH_PRIME_COST_PROPERTY_ID, dish);
                        primeCostOfOrder += Double.valueOf(param.getValue()) * Double.valueOf(dishAmountMap.get(dish));
                    }

                    BigInteger report_object_id =
                            oracleDAOFactory.getObjectDAO().
                            create(new DBObject(PluginConst.REPORT_OBJECT_TYPE_ID, "PEPORT " + name, ""));

                    propertyValueDAO.create(new Parameter(PluginConst.REPORT_DATE_PROPERTY_ID,
                            null, report_object_id, null, date));
                    propertyValueDAO.create(new Parameter(PluginConst.REPORT_ORDER_COST_PROPERTY_ID,
                            orderCost, report_object_id, null));
                    propertyValueDAO.create(new Parameter(PluginConst.REPORT_PRIME_ORDER_COST_PROPERTY_ID,
                            primeCostOfOrder + "", report_object_id, null));

                    objectReferencesDAO.create(
                            new DBObjectReference(object_id,
                            report_object_id,
                            PluginConst.LINK_ON_REPORT_PROPERTY_ID));

                } else {
                    req.setAttribute(PluginConst.ERROR_MESSAGE, errorMessage);

                    req.setAttribute(SELECTED_DISH_LIST, dishIdList);
                    req.setAttribute(SELECTED_DISH_AMOUNT_MAP, dishAmountMap);

                    return getAddPage(req);
                }


            } catch (SQLException ex) {
                logger.debug("Обшибка при проставноке уровня транзакции.", ex);
                throw new DAOException(ex);
            } finally {
                try {
                    oracleDAOFactory.getConnection().setAutoCommit(true);
                    oracleDAOFactory.getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
                } catch (SQLException ex) {
                    logger.debug("Обшибка при проставноке уровня транзакции.", ex);
                    throw new DAOException(ex);
                }
            }




//            propertyValueDAO.create(new Parameter(PluginConst.RECIPE_PROPERTY_ID, recipeXMLString, object_id, null));

            //TODO::[High] добавить код, которые переносит данные в историю.
        } else {
            throw new WrongPageRequest("Тип блюда с таким id=" + objectTypeId + " не существует. ");
        }



        return new PluginRouter("order-action.do"
                + "?"
                + PluginConst.SUBACTION
                + "="
                + PluginConst.GET_LIST, false);
    }

    /**
     * Метод, который позволяет перейти на страницу удаления элемента.
     *
     * @param req Реквест.
     * @return PluginRouter c запрашиваемой страницей.
     * @throws DAOException Ошибка доступа к базе данных.
     * @throws WrongPageRequest Критическая ошибка в данных реквеста.
     */
    private PluginRouter getDeletePage(HttpServletRequest req) throws DAOException, WrongPageRequest {
        String objecеIdValue = req.getParameter(PluginConst.OBJECT_ID);
        if ((objecеIdValue == null) || (objecеIdValue.equals(""))) {
            throw new WrongPageRequest("ID объекта не занад!");
        }
        BigInteger objecеId =
                new BigInteger(objecеIdValue);
        DBObject object = oracleDAOFactory.getObjectDAO().getById(objecеId);
        if (object != null) {
            req.setAttribute(PluginConst.OBJECT, object);
            return new PluginRouter("/Designer/Dishes/DeleteDish.jsp");
        } else {
            throw new WrongPageRequest("Блюад с таким id=" + objecеId + " не существует. ");
        }

    }

    /**
     * Метод, удаляющий данные из базы данных.
     *
     * @param req Реквест.
     * @return PluginRouter c запрашиваемой страницей.
     * @throws DAOException Ошибка доступа к базе данных.
     * @throws WrongPageRequest Критическая ошибка в данных реквеста.
     */
    private PluginRouter deleteData(HttpServletRequest req) throws DAOException, WrongPageRequest {
        String objectIdValue = req.getParameter(PluginConst.OBJECT_ID);
        if ((objectIdValue == null) || (objectIdValue.equals(""))) {
            throw new WrongPageRequest("ID объекта не занад!");
        }
        BigInteger objectId =
                new BigInteger(objectIdValue);

        DBObject object = oracleDAOFactory.getObjectDAO().getById(objectId);
        if (object != null) {
            oracleDAOFactory.getObjectDAO().delete(object.getObject_id());
            return new PluginRouter("order-action.do"
                    + "?"
                    + PluginConst.SUBACTION
                    + "="
                    + PluginConst.GET_LIST, false);
        } else {
            throw new WrongPageRequest("Заказ с таким id=" + objectId + " не существует. ");
        }

    }

    /**
     * Метод, который позволяет перейти на страницу редактирования данных.
     *
     * @param req Реквест.
     * @return PluginRouter c запрашиваемой страницей.
     * @throws DAOException Ошибка доступа к базе данных.
     * @throws WrongPageRequest Критическая ошибка в данных реквеста.
     */
    private PluginRouter getEditPage(HttpServletRequest req) throws DAOException, WrongPageRequest {
        String objectIdValue = req.getParameter(PluginConst.OBJECT_ID);
        if ((objectIdValue == null) || (objectIdValue.equals(""))) {
            throw new WrongPageRequest("ID объекта не задан!");
        }
        //Получаем id блюда.
        BigInteger objectId =
                new BigInteger(objectIdValue);
        DBObject object = oracleDAOFactory.getObjectDAO().getById(objectId);

        if (object != null) {
            //Сохраняем объектный тип блюда в реквест.
            req.setAttribute(PluginConst.OBJECT, object);

            req.setAttribute(PluginConst.OBJECT_TYPE, oracleDAOFactory.getObjectTypesDAO().getById(object.getObject_type_id()));

            List<DBObjectType> productTypeList =
                    oracleDAOFactory.getObjectTypesDAO().getObjectTypeHeirsButNotDelete(PluginConst.PRODUCTS_OBJECT_TYPE_ID);

            //Сохраняем на страницу список типов продуктов.
            req.setAttribute(DISH_OBJECT_TYPE_LIST, productTypeList);

            List<DBObject> productList;
            List<Parameter> productParameterList;
            BigInteger productObjectTypeId;

            ObjectDAO objectDAO = oracleDAOFactory.getObjectDAO();
            ParametersDAO paramDAO = oracleDAOFactory.getParameterDAO();
            //Для каждого типа продукта добавляем в реквест список продуктов этого типа и параметры этих продуктов.
            for (DBObjectType productObjectType : productTypeList) {
                productObjectTypeId = productObjectType.getObject_type_id();
                productList = objectDAO.getAllObjectByObjectTypeButNotDelete(productObjectTypeId);
                //Добавляем для типа продукта список продуктов.
                req.setAttribute(productObjectTypeId.toString(), productList);
                for (DBObject product : productList) {
                    //Для каждого продукта добавляем список его параметров.
                    productParameterList = paramDAO.getPropertyValueForObject(product.getObject_id());
                    req.setAttribute(DISH_PARAMETER_LIST_FOR_PRODUCT + "=" + product.getObject_id().toString(), productParameterList);
                }
            }

            //Сбор данных для отображения положения дел для данного блюда.
            //Работаем с параметрами объекта - блюда.
            List<Parameter> parameterList = oracleDAOFactory.getParameterDAO().getPropertyValueForObject(objectId);

            for (Parameter param : parameterList) {
                if (param.getProperty_id().equals(PluginConst.DIST_COST_PROPERTY_ID)) {
                    //Если это цена в меню блюда - записываем её в реквест.
                    req.setAttribute(PluginConst.DISH_COST, param);
                }
                if (param.getProperty_id().equals(PluginConst.RECIPE_PROPERTY_ID)) {
                    //Если это рецепт, то работаем с рецептом.
                    String recipeXML = param.getValue();
                    SAXBuilder builder = new SAXBuilder();
                    try {
                        //Разбираем рецепт.
                        //Парсим отношение продуктов и их количества из xml.
                        Document document = builder.build(new ByteArrayInputStream(recipeXML.getBytes()));
                        Element root = document.getRootElement();
                        List<Element> productElementList = root.getChildren(DISH_XML_PRODUCT_ELEMENT);

                        Map<BigInteger, String> productAmount = new HashMap<BigInteger, String>();
                        BigInteger productId;
                        for (Element productElement : productElementList) {
                            productId = new BigInteger(
                                    productElement.getAttributeValue(DISH_XML_PRODUCT_ID_ATTRIBUTE));

                            productAmount.put(productId, productElement.getAttributeValue(DISH_XML_PRODUCT_AMOUNT_ATTRIBUTE));
                        }

                        req.setAttribute(PluginConst.PRODUCT_AMOUNT_MAP, productAmount);


                        ObjectReferencesDAO objectReferencesDAO = oracleDAOFactory.getObjectReferencesDAO();

                        List<DBObjectReference> objectReferencesList = objectReferencesDAO.getAllReferenceForObject(objectId);

                        req.setAttribute(PluginConst.RECIPE_PRODUCT_LINK_LIST, objectReferencesList);


                    } catch (JDOMException ex) {
                        logger.debug("Ошибка во время разбора рецепта.", ex);
                    } catch (IOException ex) {
                        logger.debug("Ошибка во время разбора рецепта.", ex);
                    }
                }
            }


            return new PluginRouter("/Designer/Dishes/EditDish.jsp");
        } else {
            throw new WrongPageRequest("Блюда с таким id=" + objectId + " не существует. ");
        }

    }

    private int findReferenceIdInListOfReference(List<DBObjectReference> referenceList, BigInteger referenceId) {

        int i = 0;
        int size = referenceList.size();
        boolean isFound = false;

        DBObjectReference reference;

        while ((!isFound) && (i < size)) {
            reference = referenceList.get(i);
            if (reference.getReference_object_id().equals(referenceId)) {
                isFound = true;
            } else {
                i++;
            }
        }

        if (isFound) {
            return i;
        } else {
            return -1;
        }
    }

    /**
     * Сохранение данных.
     *
     * @param req Реквест.
     * @return PluginRouter c запрашиваемой страницей.
     * @throws DAOException Ошибка доступа к базе данных.
     * @throws WrongPageRequest Критическая ошибка в данных реквеста.
     */
    private PluginRouter saveData(HttpServletRequest req) throws DAOException, WrongPageRequest {
        String objectIdValue = req.getParameter(PluginConst.OBJECT_ID);
        if ((objectIdValue == null) || (objectIdValue.equals(""))) {
            throw new WrongPageRequest("ID объекта не задан!");
        }
        //Получаем объектный тип блюда.
        BigInteger objectId =
                new BigInteger(objectIdValue);
        DBObject object = oracleDAOFactory.getObjectDAO().getById(objectId);

        String name = req.getParameter(PluginConst.OBJECT_NAME);
        String description = req.getParameter(PluginConst.OBJECT_DESCRIPTION);
        String dishCost = req.getParameter(PluginConst.DISH_COST);

        String[] productIds = req.getParameterValues(DISH_IDS);
        int productNumber = productIds.length;

        Document document = new Document();
        Element rootElement = new Element(DISH_XML_ROOT_ELEMENT);
        String productCost;
        Element productElement;
        if (object != null) {

            object.setName(name);
            object.setDescription(description);

            oracleDAOFactory.getObjectDAO().update(object);

            ObjectReferencesDAO objectReferencesDAO = oracleDAOFactory.getObjectReferencesDAO();

            List<DBObjectReference> oldBObjectReferenceList = objectReferencesDAO.getAllReferenceForObject(objectId);

            int position;
            BigInteger productId;

            for (int i = 0; i < productNumber; i++) {
                productCost = req.getParameter(PluginConst.AMOUNT + productIds[i]);
                productElement = new Element(DISH_XML_PRODUCT_ELEMENT);
                productElement.setAttribute(DISH_XML_PRODUCT_ID_ATTRIBUTE, productIds[i]);
                productElement.setAttribute(DISH_XML_PRODUCT_AMOUNT_ATTRIBUTE, productCost);
                rootElement.addContent(productElement);

                productId = new BigInteger(productIds[i]);
                position = findReferenceIdInListOfReference(oldBObjectReferenceList, new BigInteger(productIds[i]));

                //Обрабатываем ссылки на продукты. (Которые уже существуют.)
                if (position != -1) {
                    oldBObjectReferenceList.remove(position);
                } else {
                    objectReferencesDAO.create(
                            new DBObjectReference(objectId,
                            new BigInteger(productIds[i]),
                            PluginConst.LINK_ON_PRODUCT_PRODPERTY_ID));
                }
            }

            //Удаляем не нужные ссылки на продукты.
            for (DBObjectReference obsoletReference : oldBObjectReferenceList) {
                objectReferencesDAO.delete(obsoletReference.getObject_reference_id());
            }

            document.setContent(rootElement);

            XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
            String recipeXMLString = outputter.outputString(document);

            ParametersDAO propertyValueDAO = oracleDAOFactory.getParameterDAO();

            List<Parameter> parameterList = propertyValueDAO.getPropertyValueForObject(objectId);

            for (Parameter parameter : parameterList) {
                if (parameter.getProperty_id().equals(PluginConst.DIST_COST_PROPERTY_ID)) {
                    parameter.setValue(dishCost);
                    propertyValueDAO.update(parameter);
                }
                if (parameter.getProperty_id().equals(PluginConst.RECIPE_PROPERTY_ID)) {
                    parameter.setValue(recipeXMLString);
                    propertyValueDAO.update(parameter);
                }
            }


            //TODO::[High] добавить код, которые переносит данные в историю.
        } else {
            throw new WrongPageRequest("Тип блюда с таким id=" + objectId + " не существует. ");
        }


        return new PluginRouter("dishes-action.do"
                + "?"
                + PluginConst.SUBACTION
                + "="
                + PluginConst.GET_VIEW_PAGE
                + "&"
                + PluginConst.OBJECT_ID
                + "="
                + object.getObject_id(), false);

    }

    private PluginRouter getViewPage(HttpServletRequest req) throws WrongPageRequest, DAOException {
        String objectIdValue = req.getParameter(PluginConst.OBJECT_ID);
        if ((objectIdValue == null) || (objectIdValue.equals(""))) {
            throw new WrongPageRequest("ID объекта не занад!");
        }
        BigInteger objectId =
                new BigInteger(objectIdValue);
        DBObject object = oracleDAOFactory.getObjectDAO().getById(objectId);

        if (object != null) {
            //Записали объектный тип в реквест.
            DBObjectType objectType = oracleDAOFactory.getObjectTypesDAO().getById(object.getObject_type_id());
            req.setAttribute(PluginConst.OBJECT_TYPE, objectType);
            //Записали объект в реквест.
            req.setAttribute(PluginConst.OBJECT, object);
            //Работаем с параметрами объекта - блюда.
            List<Parameter> parameterList = oracleDAOFactory.getParameterDAO().getPropertyValueForObject(objectId);

            for (Parameter param : parameterList) {
                if (param.getProperty_id().equals(PluginConst.ORDER_CONST_PROPERTY_ID)) {
                    //Если это цена в меню блюда - записываем её в реквест.
                    req.setAttribute(PluginConst.ORDER_COST, param);
                }
                if (param.getProperty_id().equals(PluginConst.ORDER_DATE_INCOME_PROPERTY_ID)) {

                    req.setAttribute(PluginConst.ORDER_DATE, param);
                }
                if (param.getProperty_id().equals(PluginConst.ORDER_STATUS_PROPERTY_ID)) {

                    req.setAttribute(PluginConst.ORDER_STATUS, param);
                }

                if (param.getProperty_id().equals(PluginConst.ORDER_CONSIST_PROPERTY_ID)) {
                    //Если это заказ, то работаем с ним.
                    String recipeXML = param.getValue();
                    SAXBuilder builder = new SAXBuilder();
                    try {
                        //Разбираем рецепт.
                        //Парсим отношение блюд и их количества из xml.
                        Document document = builder.build(new ByteArrayInputStream(recipeXML.getBytes()));
                        Element root = document.getRootElement();
                        List<Element> dishElementList = root.getChildren(DISH_XML_PRODUCT_ELEMENT);

                        Map<BigInteger, String> dishAmount = new HashMap<BigInteger, String>();
                        BigInteger productId;
                        for (Element productElement : dishElementList) {
                            productId = new BigInteger(
                                    productElement.getAttributeValue(DISH_XML_PRODUCT_ID_ATTRIBUTE));

                            dishAmount.put(productId, productElement.getAttributeValue(DISH_XML_PRODUCT_AMOUNT_ATTRIBUTE));
                        }

                        //Собираем ссылки на блюда.
                        List<DBObject> dishList = new LinkedList<DBObject>();

                        ObjectReferencesDAO objectReferencesDAO = oracleDAOFactory.getObjectReferencesDAO();
                        ObjectDAO objectDAO = oracleDAOFactory.getObjectDAO();

                        List<DBObjectReference> objectReferencesList =
                                objectReferencesDAO.getListByObjectIDAndPropertyID(objectId,
                                PluginConst.LINK_ON_DISH_PRODPERTY_ID);

                        for (DBObjectReference objectReference : objectReferencesList) {
                            dishList.add(objectDAO.getById(objectReference.getReference_object_id()));
                        }

                        //Поднимаем объектные типы - типы продуктов для создания иерархии.
                        List<DBObjectType> productTypeList = new LinkedList<DBObjectType>();

                        Map<BigInteger, List<DBObject>> dishTypeProductsMap = new HashMap<BigInteger, List<DBObject>>();

                        BigInteger dishTypeID;
                        List<DBObject> dishForDishTypeList;

                        ObjectTypesDAO objectTypesDAO = oracleDAOFactory.getObjectTypesDAO();

                        for (DBObject dish : dishList) {
                            dishTypeID = dish.getObject_type_id();

                            if (dishTypeProductsMap.containsKey(dishTypeID)) {
                                dishForDishTypeList = dishTypeProductsMap.get(dishTypeID);
                                dishForDishTypeList.add(dish);
                            } else {
                                productTypeList.add(objectTypesDAO.getById(dishTypeID));
                                dishForDishTypeList = new LinkedList<DBObject>();
                                dishForDishTypeList.add(dish);
                                dishTypeProductsMap.put(dishTypeID, dishForDishTypeList);
                            }
                        }


                        //Сохраняем на страницу список типов блюд.
                        req.setAttribute(DISH_OBJECT_TYPE_LIST, productTypeList);

                        List<Parameter> dishParameterList;
                        BigInteger dishObjectTypeId;

                        ParametersDAO paramDAO = oracleDAOFactory.getParameterDAO();

                        double primePrice = 0;

                        //Для каждого типа блюда добавляем в реквест список блюд этого типа и параметры этих блюд.
                        for (DBObjectType dishObjectType : productTypeList) {
                            dishObjectTypeId = dishObjectType.getObject_type_id();
                            dishList = dishTypeProductsMap.get(dishObjectTypeId);
                            //Добавляем для типа блюда список блюд.
                            req.setAttribute(dishObjectTypeId.toString(), dishList);
                            for (DBObject product : dishList) {
                                //Для каждого блюда добавляем список его параметров.
                                dishParameterList = paramDAO.getPropertyValueForObject(product.getObject_id());

                                dishParameterList.add(
                                        new Parameter(
                                        null, PluginConst.ORDER_CONST_PROPERTY_ID,
                                        dishAmount.get(product.getObject_id()),
                                        null, null, null, objectIdValue, null, null));
                                req.setAttribute(DISH_PARAMETER_LIST_FOR_PRODUCT
                                        + "="
                                        + product.getObject_id().toString(), dishParameterList);
                                //Вычисляем себестоимость продукта.
                            }
                        }

                    } catch (JDOMException ex) {
                        logger.debug("Ошибка во время разбора заказа.", ex);
                    } catch (IOException ex) {
                        logger.debug("Ошибка во время разбора заказа.", ex);
                    } catch (Exception ex) {
                        logger.debug(ex);
                    }
                }
            }

            return new PluginRouter("/Order/ViewOrder.jsp");
        } else {
            throw new WrongPageRequest("Заказа с таким id=" + objectId + " не существует. ");
        }
    }

    private Parameter foundCostProductParameter(List<Parameter> listOfParameter) {

        int i = 0;
        int size = listOfParameter.size();
        boolean isFound = false;
        Parameter param = null;

        while ((!isFound) && (i < size)) {
            param = listOfParameter.get(i);
            if (param.getProperty_id().equals(PluginConst.COST_PROPERTY_ID)) {
                isFound = true;
            } else {
                i++;
            }
        }

        return param;
    }

    private PluginRouter finishOrder(HttpServletRequest req) throws WrongPageRequest, DAOException {
        String objectIdValue = req.getParameter(PluginConst.OBJECT_ID);
        if ((objectIdValue == null) || (objectIdValue.equals(""))) {
            throw new WrongPageRequest("ID объекта не занад!");
        }
        BigInteger objectId =
                new BigInteger(objectIdValue);

        DBObject object = oracleDAOFactory.getObjectDAO().getById(objectId);
        if (object != null) {
            Parameter param = oracleDAOFactory.getParameterDAO().getParameterByIdForObject(PluginConst.ORDER_STATUS_PROPERTY_ID, objectId);
            param.setProperty_list_value_id(PluginConst.ORDER_STATUS_FINISHED_LIST_VALUE_ID);
            oracleDAOFactory.getParameterDAO().update(param);
            return new PluginRouter("order-action.do"
                    + "?"
                    + PluginConst.SUBACTION
                    + "="
                    + PluginConst.GET_LIST, false);
        } else {
            throw new WrongPageRequest("Заказ с таким id=" + objectId + " не существует. ");
        }

    }

    private PluginRouter terminateOrder(HttpServletRequest req) throws WrongPageRequest, DAOException {
        String objectIdValue = req.getParameter(PluginConst.OBJECT_ID);
        if ((objectIdValue == null) || (objectIdValue.equals(""))) {
            throw new WrongPageRequest("ID объекта не занад!");
        }
        BigInteger objectId =
                new BigInteger(objectIdValue);

        DBObject object = oracleDAOFactory.getObjectDAO().getById(objectId);
        if (object != null) {
            Parameter param = oracleDAOFactory.getParameterDAO().getParameterByIdForObject(PluginConst.ORDER_STATUS_PROPERTY_ID, objectId);
            param.setProperty_list_value_id(PluginConst.ORDER_STATUS_TERMINATE_LIST_VALUE_ID);
            oracleDAOFactory.getParameterDAO().update(param);

            ParametersDAO parameterDAO = oracleDAOFactory.getParameterDAO();
            ObjectReferencesDAO referenceDAO = oracleDAOFactory.getObjectReferencesDAO();
            ObjectDAO objectDAO = oracleDAOFactory.getObjectDAO();

            DBObjectReference reportObject = referenceDAO.getByObjectIDAndPropertyID(objectId, PluginConst.LINK_ON_REPORT_PROPERTY_ID);

            objectDAO.delete(reportObject.getReference_object_id());

            List<DBObjectReference> listOfObjectReference = referenceDAO.getListByObjectIDAndPropertyID(objectId,
                    PluginConst.LINK_ON_EXPENSE_PRODPERTY_ID);
            try {
                oracleDAOFactory.getConnection().setAutoCommit(false);
                oracleDAOFactory.getConnection().setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

                DBObjectReference referenceOnProduct;
                Parameter productStoreTypeParam, expenseProductAmountParam;
                for (DBObjectReference objectReference : listOfObjectReference) {
                    referenceOnProduct = referenceDAO.getByObjectIDAndPropertyID(objectReference.getReference_object_id(),
                            PluginConst.LINK_EXPENSE_ON_PRODUCT_PROPERTY_ID);
                    productStoreTypeParam = parameterDAO.getParameterByIdForObject(PluginConst.STORE_TYPE_PROPERTY_ID,
                            referenceOnProduct.getReference_object_id());
                    expenseProductAmountParam = parameterDAO.getParameterByIdForObject(PluginConst.AMOUNT_OF_PRODUCT_EXPENSE_PRODPERTY_ID,
                            objectReference.getReference_object_id());

                    productStoreTypeParam.setValue((Double.valueOf(productStoreTypeParam.getValue())
                            + Double.valueOf(expenseProductAmountParam.getValue()))
                            + "");
                    parameterDAO.update(productStoreTypeParam);

                    objectDAO.delete(objectReference.getReference_object_id());

                }
            } catch (SQLException ex) {
                logger.debug("Обшибка при проставноке уровня транзакции.", ex);
                throw new DAOException(ex);
            } finally {
                try {
//                    oracleDAOFactory.getConnection().commit();
                    oracleDAOFactory.getConnection().setAutoCommit(true);
                    oracleDAOFactory.getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
                } catch (SQLException ex) {
                    logger.debug("Обшибка при проставноке уровня транзакции.", ex);
                    throw new DAOException(ex);
                }
            }
            return new PluginRouter("order-action.do"
                    + "?"
                    + PluginConst.SUBACTION
                    + "="
                    + PluginConst.GET_LIST, false);
        } else {
            throw new WrongPageRequest("Заказ с таким id=" + objectId + " не существует. ");
        }

    }
}
